home *** CD-ROM | disk | FTP | other *** search
/ Aminet 33 / Aminet 33 - October 1999.iso / Aminet / dev / cross / ava-0.2.5.lha / ava-0.2.5 / src / Symbol.C < prev    next >
Encoding:
C/C++ Source or Header  |  1999-03-23  |  17.6 KB  |  537 lines

  1. /*
  2.   Symbol.C
  3.   
  4.   Symbols, Macros and that's all.
  5.   Uros Platise, dec. 1998
  6. */
  7.  
  8. #include "Preproc.h"
  9. #include "Lexer.h"
  10. #include "Syntax.h"
  11. #include "Segment.h"
  12. #include "Symbol.h"
  13. #include "Reports.h"
  14. #include "Object.h"
  15.  
  16. bool TSymbol::addMacro(){
  17.   TSymbolRec tmpRec;
  18.   string macroName;
  19.   parseFlags(&tmpRec,TlxData::STRING);
  20.   macroName=lxP->string; parseMacro(&tmpRec);
  21.   if (test(&tmpRec,macroName)==Define){storeSym(&tmpRec, macroName);}
  22.   if (macroName[0]=='_' && macroName[1]=='_'){return true;}
  23.   return false;
  24. }
  25.  
  26. void TSymbol::addMacro(const char* macroName, const char* label=NULL,
  27.                        TSymbolRec::TAttr macro_attr=TSymbolRec::Internal){
  28.   if (label==NULL){label=macroName;}
  29.   TSymbolRec tmpRec;
  30. //  tmpRec.attr=TSymbolRec::Internal;
  31.   tmpRec.attr=macro_attr;
  32.   tmpRec.label=false; 
  33.   tmpRec.internal=false;
  34.   tmpRec.macro=label;
  35.   if (test(&tmpRec,macroName)==Define){storeSym(&tmpRec, macroName);}
  36. }
  37.  
  38. int TSymbol::addLabel(){
  39.   TSymbolRec tmpRec;
  40.   string labelName;
  41.   char buf[128];     /* buf is used below by the getPC() */
  42.   PUT_TOKENBACK;    /* parseFlags routine immediately calls GET_TOKEN */
  43.   
  44.   parseFlags(&tmpRec,TlxData::LABEL);    
  45.   labelName=lxP->string; tmpRec.label=true;
  46.   
  47.   if (tmpRec.attr!=TSymbolRec::Extern){
  48.     tmpRec.macro=segment.getPC(buf);
  49.     tmpRec.segNo=segment.getSegNo();
  50.   }else{tmpRec.macro=labelName;}
  51.   
  52.   if (test(&tmpRec,labelName)==Define){storeSym(&tmpRec, labelName);}
  53.   return 1;
  54. }
  55.  
  56. /*
  57.   Internal Labels are intented for segment purposes.
  58.   These labels are always marked external, since linker defines
  59.   them later and are never put into object file.
  60.   
  61.   Special flag protected disallaws modifying the value from
  62.   the assembler source.
  63. */
  64. void TSymbol::addInternalLabel(const char* macroName, const char* label=NULL){
  65.   if (label==NULL){label=macroName;}
  66.   TSymbolRec tmpRec;
  67.   tmpRec.attr=TSymbolRec::Extern;
  68.   tmpRec.label=true; 
  69.   tmpRec.internal=true;
  70.   tmpRec.macro=label;
  71.   test(&tmpRec,macroName);
  72.   sym[label]=tmpRec;
  73. }
  74.  
  75. /* if sym is already present, updates refs and then stores */
  76. void TSymbol::storeSym(TSymbolRec* tmpRec, const string& macroName){
  77.   TsymI symI;
  78.   if ((symI=sym.find(macroName))!=sym.end()){
  79.     /* remove old references to old segment */
  80.     segment.incRef((*symI).second.segNo, (*symI).second.refCnt);
  81.     tmpRec->refCnt += (*symI).second.refCnt; (*symI).second = *tmpRec;
  82.   }else{sym[macroName]=*tmpRec;}
  83.   /* add new refs to new segment (if it is the same, so what) */
  84.   segment.incRef(tmpRec->segNo, tmpRec->refCnt);
  85. }
  86.  
  87. void TSymbol::modifyValue(const char* macroName, const char* macroString){
  88.   TsymI symI;
  89.   if ((symI=sym.find(macroName))==sym.end()){
  90.     throw syntax_error("Label is not defined: ", macroName);}
  91.   (*symI).second.macro = macroString; 
  92. }
  93.  
  94. bool TSymbol::ifexpr(){
  95.   GiveUndefinedSymVal(true);
  96.   lexer.gettoken();
  97.   syntax.Parse_GAS();
  98.   GiveUndefinedSymVal(false);
  99.   if (gas.status!=TGAS::Solved){
  100.     throw syntax_error("Bad format: ", gas.eqstr);}
  101.   return gas.result()>0;
  102. }
  103.  
  104. bool TSymbol::ifdef(){
  105.   lexer._gettoken();
  106.   if (lxP->type!=TlxData::STRING){
  107.     throw syntax_error("Bad format: ", lxP->string);}
  108.   return findMacro(lxP->string)!=NULL;
  109. }
  110.  
  111. string* TSymbol::findMacro(const string& macroName){
  112.   TsymI symI;
  113.   if ((symI=sym.find(macroName))==sym.end()){return NULL;}
  114.   return &(*symI).second.macro;
  115. }
  116.  
  117. void TSymbol::undefine(){
  118.   lexer._gettoken();
  119.   if (lxP->type!=TlxData::STRING){
  120.     throw syntax_error("Invalid macro declaration.");}
  121.   undefine(lxP->string);
  122. }
  123.  
  124. void TSymbol::undefine(const string& macroName){
  125.   TsymI symI;
  126.   if ((symI=sym.find(macroName))==sym.end()){
  127.     throw syntax_error("Cannot erase macro that is not defined: ", 
  128.                        macroName.c_str());}
  129.   sym.erase(symI);
  130. }
  131.  
  132. /*
  133. long TSymbol::noRefs(int segNo){
  134.   long refCnt=0;
  135.   TsymI symI;
  136.   for(symI=sym.begin(); symI != sym.end(); symI++){
  137.     refCnt += ((*symI).second.segNo==segNo)?(*symI).second.refCnt:0;}  
  138.   return refCnt;
  139. }
  140. */
  141.  
  142. int TSymbol::Replace2Zero(){
  143.   if (undefEqZero==false){return 0;}
  144.   TMemBlock* macroSource = new TMemBlock;
  145.   macroSource->append("0");
  146.   preproc.insert(macroSource);
  147.   return 1;
  148. }
  149.  
  150. int TSymbol::replaceWithMacro(){
  151.   TsymI symI;
  152.   char macroName [LX_STRLEN], macro [LX_LINEBUF];
  153.   char buf [LX_LINEBUF];
  154.   int psi_chk=0;
  155.   int parethLevel;        /* i.e.: ((( ... ))) */
  156.   
  157.   /* SPECIAL CASE: for #ifdef parser, all undefined macros should return
  158.      zero; this is handled by the second if clause! 
  159.      
  160.      Exceptions to which zero is assigned too:
  161.        * labels
  162.   */ 
  163.   if (lxP->type!=TlxData::STRING){return 0;}
  164.   if ((symI=sym.find(lxP->string))==sym.end()){return Replace2Zero();}
  165.   if ((*symI).second.label==true && undefEqZero==true){return Replace2Zero();}
  166.     
  167.   strcpy(macroName, lxP->string);
  168.   strcpy(macro,     (*symI).second.macro.c_str());
  169.   psi_chk     =     (*symI).second.noArgs;
  170.  
  171.   /* -----------------------------------------------------------------------
  172.      (Macros are used only durring PASS1, otherwise produce ERROR!)
  173.      Further test showed up that macros can be used in both passes, 1 and 2!
  174.      -----------------------------------------------------------------------          
  175. TODO:  
  176.      Add error report on every unknown symbol to replace seeking for 
  177.      undefined symbol in Object.C::obj2exe()
  178.  
  179.   if (HaltOnMacro==true && (*symI).second.label==false){
  180.     if ((*symI).second.attr&TSymbolRec::Extern){
  181.       throw syntax_error("Undefined symbol: ", macroName);}
  182.     else{
  183.       throw syntax_error("Macro should be declared before it is used: ",
  184.         macroName);
  185.     }   
  186.   }
  187.   */
  188.   
  189.   /* Segment Reference Count */
  190.   segment.incRef((*symI).second.segNo);
  191.   
  192.   /* Symbol Count - used when reporting file references and symbols */
  193.   (*symI).second.refCnt++;
  194.   
  195.   /* Non-internal Extern macros should tell the system that macro 
  196.      is present - but string is not replaced */
  197.   if (((*symI).second.attr & TSymbolRec::Extern) && 
  198.       !((*symI).second.internal) && psi_chk==0){
  199.     lxP->macro=true;
  200.     return 0;
  201.   }    
  202.  
  203.   /* SPECIAL CASE: if macroname equals macro, skip it */
  204.   if (strcmp(macro,macroName)==0 && psi_chk==0){lxP->macro=true;return 0;}
  205.  
  206.   /* check for parameters in form (A0+B0+C0+...+n0,A1+B1+C1+...+n1,...) 
  207.      where A,B and C may MATH, STRING and LVAL */
  208.   clearps(); 
  209.   if (psi_chk>0){
  210.     lexer._gettoken();
  211.     if (lxP->type==TlxData::CONTROL && lxP->string[0]=='('){
  212.       while(lexer._gettoken()){
  213.         /* load parameter string terminated by a >new line<, #, THEEND
  214.        NEWLINE or CONTROL ... except ( and ) */
  215.         buf[0]=parethLevel=0;
  216.     do{
  217.           if (lxP->type==TlxData::PREPROC || lxP->type==TlxData::THEEND){break;}
  218.           if (lxP->type==TlxData::NEWLINE){continue;}
  219.       if (lxP->type==TlxData::CONTROL){        
  220.         if (lxP->string[0]=='('){parethLevel++;}
  221.         else if (lxP->string[0]==')'){
  222.           if (parethLevel==0){break;}    /* end of parameters */
  223.           parethLevel--;
  224.         }else{break;}    /* if none of above CONTROL ch is found */
  225.       }
  226.           strcat(buf,lxP->string);
  227.         }while(lexer._gettoken());
  228.  
  229.         if (buf[0]==0){throw syntax_error("Parameter is expected.");}      
  230.         addps(buf);
  231.         if (lxP->type==TlxData::CONTROL && lxP->string[0]==')'){break;}
  232.         if (lxP->type!=TlxData::CONTROL || lxP->string[0]!=','){
  233.          throw syntax_error("Invalid delimiter after parameter: ",lxP->string);}
  234.       } 
  235.     }
  236.     if (psi_chk!=psi){throw syntax_error("Invalid parameter count.");}
  237.     replaceParameters(macro);
  238.   }
  239.  
  240.   TMemBlock* macroSource = new TMemBlock;
  241.   macroSource->append(macro);
  242.   preproc.insert(macroSource);
  243.   return 1;
  244. }
  245.  
  246. /*
  247.   Error Table tests attributes of stored and new (template) symbol.
  248.   Template symbol is represented in rows and stored symbol in columns.
  249. */
  250. TSymbol::TOperation TSymbol::ErrorTable[16] = {
  251.     OK,    Define,    Define,    Error1,
  252.     OK,    Common,    Error2,    Error2,
  253.     OK,    Define,    Define,    Error1,
  254.     Error1,    Define,    Error1,    Define
  255. };
  256.  
  257. #define stRec (*symI).second
  258.  
  259. TSymbol::TOperation TSymbol::test(TSymbolRec* pRec, const string& macroName){
  260.   TsymI symI;
  261.   /* Calculate offset for error check table */
  262.   pRec->tbl_offset = pRec->attr;
  263.   if (pRec->attr & TSymbolRec::Extern && pRec->attr & TSymbolRec::Virtual){
  264.     pRec->tbl_offset-=2;}
  265.   pRec->tbl_offset--;
  266.   
  267.   if ((symI=sym.find(macroName))==sym.end()){return Define;}
  268.  
  269.   if ((stRec.internal==true)^(pRec->internal==true)){
  270.     throw syntax_error("Source contains symbols that are used by segments: ",
  271.       macroName.c_str());
  272.   }
  273.   int opRes;
  274.   if (stRec.attr&TSymbolRec::Virtual){
  275.     reports.Warnning(TReports::Symbols,
  276.       "Virtual symbol `%s' is assigned new value.",macroName.c_str());
  277.   }    
  278.   if (stRec.attr==TSymbolRec::Internal){
  279.     throw syntax_error("Symbol already defined: ",macroName.c_str());}    
  280.   if (pRec->attr==TSymbolRec::Internal){
  281.     if (stRec.attr==TSymbolRec::Virtual ||
  282.         stRec.attr==TSymbolRec::Extern){return Define;}
  283.     else{opRes=Error1;}
  284.   }else{opRes = ErrorTable[pRec->tbl_offset+(stRec.tbl_offset<<2)];}
  285.   
  286.   /* Test func() already handles the following cases */
  287.   switch(opRes){
  288.   case Common:
  289.     if (stRec.macro != pRec->macro){
  290.       reports.Warnning(TReports::Symbols,
  291.         "Previously decalred symbol `%s' as `%s'\n"
  292.         "          is now redefined to `%s'", 
  293.     macroName.c_str(), stRec.macro.c_str(), pRec->macro.c_str());
  294.       throw syntax_error("Symbol already defined: ", macroName.c_str());
  295.     }
  296.     break;      
  297.   case Error1:
  298.     throw syntax_error("Attributes missmatch with"
  299.                        " previously declared symbol: ",macroName.c_str());
  300.   case Error2:
  301.     throw syntax_error("Symbol already defined: ",macroName.c_str());
  302.   }
  303.   /* UPDATE REFERENCES */
  304.   if (opRes==OK){
  305.     int savedRef = pRec->refCnt; *pRec = stRec; pRec->refCnt = savedRef;
  306.     storeSym(pRec, macroName);
  307.   }
  308.   return opRes;
  309. }
  310.  
  311. /* returns macro name in lxP->string */ 
  312. void TSymbol::parseFlags(TSymbolRec* pRec, int terminator_type){
  313.   while(lexer._gettoken()){    
  314.     if (lxP->type!=TlxData::STRING && lxP->type!=terminator_type){
  315.       throw syntax_error("Invalid macro declaration.");}
  316.       
  317.     if (strcmp(lxP->string,"extern")==0){pRec->attr|=TSymbolRec::Extern;}
  318.     else if (strcmp(lxP->string,"public")==0){pRec->attr|=TSymbolRec::Public;}
  319.     else if (strcmp(lxP->string,"virtual")==0){pRec->attr|=TSymbolRec::Virtual;}
  320.     else if (strcmp(lxP->string,"ref")==0){parseReference(pRec);}
  321.     else if (lxP->type==terminator_type){break;}
  322.     else{
  323.       /* if label own attributes, listed just before, : is not
  324.          need - otherwise : is required! */
  325.       if (terminator_type==TlxData::LABEL){
  326.         if (pRec->attr==0){
  327.           throw syntax_error("Semicolon : is missing after label: ",
  328.                           lxP->string);}
  329.         break; 
  330.       }else{throw syntax_error("Invalid macro attribute.");}
  331.     }
  332.   }
  333.   /* check relations between flags */
  334.   if (pRec->attr==0||
  335.       pRec->attr==TSymbolRec::Extern||
  336.       pRec->attr==TSymbolRec::Public||
  337.       pRec->attr==TSymbolRec::Virtual||
  338.       (pRec->attr & TSymbolRec::Extern && pRec->attr & TSymbolRec::Virtual)){
  339.     return;}    
  340.   throw syntax_error("Symbol attributes are excluding each other.");
  341. }
  342.  
  343. void TSymbol::parseMacro(TSymbolRec* pRec){
  344.   bool newLineExpected=false;        /* if macro is extended with \ */
  345.   
  346.   /* check for parameters in form (a0,a1,...,an) */
  347.   clearps(); lexer._gettoken();
  348.   if (lxP->type==TlxData::CONTROL && lxP->string[0]=='(' && lxdata.stick){
  349.     while(lexer._gettoken()){
  350.       while (lxP->type==TlxData::NEWLINE){lexer._gettoken();}
  351.       if (lxP->type!=TlxData::STRING){
  352.         throw syntax_error("Parameter is expected.");}
  353.       addps(lxP->string);
  354.       lexer._gettoken();
  355.       while (lxP->type==TlxData::NEWLINE){lexer._gettoken();}      
  356.       if (lxP->type==TlxData::CONTROL && lxP->string[0]==')'){break;}
  357.       if (lxP->type!=TlxData::CONTROL || lxP->string[0]!=','){
  358.         throw syntax_error("Invalid delimiter after macro parameter: ",
  359.                            lxP->string);
  360.       }
  361.     } 
  362.   }else{PUT_TOKENBACK;}
  363.   pRec->noArgs = psi;
  364.  
  365.   /* load macro string terminated by a new line, # or THEEND */
  366.   while(lexer._gettoken()){
  367.     if (lxP->type==TlxData::PREPROC || lxP->type==TlxData::THEEND){break;}
  368.     if (lxP->type==TlxData::NEWLINE){
  369.       if (newLineExpected==true){newLineExpected=false;}else{break;}}
  370.     if (lxP->type==TlxData::CONTROL && lxP->string[0]=='\\'){
  371.       newLineExpected=true;continue;}
  372.     replace(lxP->string);      
  373.     if (lxP->type==TlxData::QSTRING){
  374.       pRec->macro = pRec->macro + '"' + lxP->string + '"';
  375.     } else {
  376.       if (lxP->stick==false && pRec->macro.size()>0){pRec->macro += " ";}
  377.       pRec->macro = pRec->macro + lxP->string;      
  378.     }
  379.   }
  380.   /* add default value 1 */
  381.   if (pRec->macro.length()==0){pRec->macro="1";}
  382. }
  383.  
  384. void TSymbol::reparseMacro(TSymbolRec* pRec){
  385.   /* load macro string terminated by a new line, # or THEEND */
  386.   while(lexer.gettoken()){
  387.     if (lxP->type==TlxData::PREPROC || lxP->type==TlxData::THEEND){break;}
  388.     if (lxP->type==TlxData::QSTRING){
  389.       pRec->macro = pRec->macro + '"' + lxP->string + '"';
  390.     }else{pRec->macro = pRec->macro + lxP->string + " ";}
  391.   }
  392. }
  393.  
  394. void TSymbol::parseReference(TSymbolRec* pRec){
  395.   /* expecting = */
  396.   GET_TOKEN;
  397.   if (lxP->type!=TlxData::CONTROL || strcmp(lxP->string,"=")){
  398.     throw syntax_error("Bad format at ",lxP->string);}
  399.     
  400.   char fn_buf [LX_STRLEN];
  401.   syntax.Parse_FileName(fn_buf);
  402.   pRec->ref = fn_buf;
  403. }
  404.  
  405. void TSymbol::addps(char *s){
  406.   if (psi==SYM_MAX_PARAM){
  407.     throw generic_error("Parameter stack too small."
  408.       " Increase SYM_MAX_PARAM in Symbol.h.");}
  409.   /* SPECIAL CASE: NEGATIVE VALUES MUST BE IN BRACKETS! */
  410.   if (s[0]=='-'){
  411.     param_stack[psi][0]='(';
  412.     strcpy(¶m_stack[psi][1],s); strcat(param_stack[psi++],")");
  413.   }else{strcpy(param_stack[psi++], s);}
  414. }
  415.  
  416. /* 
  417.   Replace parameter with sequent number - before storing it to the
  418.   database.
  419. */
  420. void TSymbol::replace(char *s){
  421.   int i; for (i=0;i<psi;i++){
  422.     if (strcmp(s,param_stack[i])==0){sprintf(s,"g%.2x",i);}}
  423. }
  424.  
  425. void TSymbol::replaceParameters(char* s){
  426.   char *needle,*src,buf [LX_LINEBUF],pnum [5];
  427.   int pi;
  428.   for (pi=0;pi<psi;pi++){
  429.     buf[0]=0;src=s; sprintf(pnum,"g%.2x",pi);  
  430.     while ((needle=strstr(src,pnum))!=NULL){
  431.       strncat(buf,src,needle-src); strcat(buf,param_stack[pi]);
  432.       src+=needle-src+3;
  433.     }
  434.     /* copy rest of it and update s */
  435.     strcat(buf,src); strcpy(s,buf);
  436.   }
  437. }
  438.  
  439. /* Export all symbols, except extern with refCnt=0 */
  440. void TSymbol::saveSymbols(){
  441.   TsymCI symI;
  442.   /* export public, extern and extern virtual */
  443.   for (symI=sym.begin(); symI != sym.end(); symI++){  
  444.   
  445.     if ((stRec.attr==TSymbolRec::Extern && stRec.refCnt==0) || 
  446.         stRec.attr==TSymbolRec::Internal ||
  447.     stRec.attr==TSymbolRec::Virtual ||
  448.       /*  stRec.label==false || */ stRec.internal==true){continue;}
  449.       
  450.     object.outOperand(stRec.refCnt);
  451.     object.outOperand(stRec.segNo);
  452.     object.outOperand(stRec.attr);    
  453.     object.outStringOperand((*symI).first.c_str());
  454.     object.outStringOperand(stRec.macro.c_str());
  455.     object.outSymbolData();
  456.   }  
  457.   object.outTerminator();
  458.   /* export internal and virtual */
  459.   for(symI=sym.begin(); symI != sym.end(); symI++){
  460.     if (!(stRec.attr==TSymbolRec::Internal ||
  461.         stRec.attr==TSymbolRec::Virtual) ||
  462.     stRec.label==false || stRec.internal==true){continue;}
  463.     object.outOperand(stRec.refCnt);
  464.     object.outOperand(stRec.segNo);
  465.     object.outOperand(stRec.attr);
  466.     object.outStringOperand((*symI).first.c_str());
  467.     object.outOperand(stRec.macro.c_str());    
  468.     object.outSymbolData();
  469.   }  
  470. }
  471.  
  472. /* Load public and extern virtual. */
  473. void TSymbol::loadSymbols(){
  474.   TSymbolRec tmpRec;
  475.   tmpRec.label = true;
  476.   tmpRec.segP = preproc.csrc->segP;
  477.   tmpRec.attr = object.popOperand();
  478.   tmpRec.segNo = preproc.csrc->segP->seg[object.popOperand()].newNo;
  479.   tmpRec.refCnt = object.popOperand();
  480.   tmpRec.macro = object.popStringOperand();  
  481.   string macroName = object.popStringOperand();
  482. /*
  483.   printf("adding `%s' = `%s' (segNo=%d, refCnt=%d, attr=%d)\n",
  484.     macroName.c_str(), 
  485.     tmpRec.macro.c_str(),
  486.     tmpRec.segNo, tmpRec.refCnt, tmpRec.attr); 
  487. */
  488.   if (test(&tmpRec,macroName)==Define){storeSym(&tmpRec,macroName);}
  489. }
  490.  
  491. /* Load non-public symbols and save them back immediatelly - debug support */
  492. void TSymbol::loadNonPublicSymbols(){
  493.   TSymbolRec tmpRec;
  494.   long lval;
  495.   string macroName = object.popStringOperand();
  496.   lval = object.popOperand();
  497.   tmpRec.attr = object.popOperand();
  498.   tmpRec.segNo = preproc.csrc->segP->seg[object.popOperand()].newNo;
  499.   tmpRec.refCnt = object.popOperand();
  500.   if (segment.isEnabled(tmpRec.segNo)==false){return;}
  501.   object.outOperand(tmpRec.refCnt);
  502.   object.outOperand(tmpRec.segNo);
  503.   object.outOperand(tmpRec.attr);
  504.   object.outStringOperand(macroName.c_str());
  505.   object.outOperand(lval);    
  506.   object.outSymbolData();
  507. }
  508.  
  509. /* After segments are fitted, public symbols need to be updated.
  510.    A pass is required for each file seperately, because segment labels
  511.    need to be adjusted every turn. 
  512.    
  513.    Extern virtual syms are changed to internal ones.
  514.    
  515.    Every memory block ends with # character! 
  516.    While updatePublic() is called continously, this character is
  517.    removed by first get_token in the reparseMacro() func. 
  518.    
  519.    For the last time, Object.C must take care, that this
  520.    character does not confuse it. */
  521. void TSymbol::updatePublic(){
  522.   TsymI symI;
  523.   /* compare segP to find current file */
  524.   for(symI=sym.begin(); symI != sym.end(); symI++){
  525.     if (stRec.segP != preproc.csrc->segP){continue;}
  526.     /* copy macroString and reparse it */ 
  527.     TMemBlock* macroSource = new TMemBlock;
  528.     macroSource->append(stRec.macro.c_str());
  529.     macroSource->append(" #");    /* add delimiter so we know where to stop */
  530.     preproc.insert(macroSource);
  531.     TSymbolRec tmp; reparseMacro(&tmp); stRec.macro=tmp.macro;
  532.     if (stRec.attr==(TSymbolRec::Extern+TSymbolRec::Virtual)){
  533.       stRec.attr=TSymbolRec::Internal;}
  534.   }
  535. }
  536.  
  537.